//
//  Movie.m
//  justTest
//
//  Created by 小零钱 on 2018/2/28.
//  Copyright © 2018年 小零钱. All rights reserved.
//

/*
 实现NSCoding的自动归档和解档
 
 如果你实现过自定义模型数据持久化的过程，那么你也肯定明白，如果一个模型有许多个属性，那么我们需要对每个属性都实现一遍encodeObject 和 decodeObjectForKey方法，如果这样的模型又有很多个，真的是一个十分麻烦的事情。不过如果你会使用runtime，这里就有更简便的方法。
 */

#import "Movie.h"
#import <objc/message.h>

// 优化：下面是encodeWithCoder 和 initWithCoder这两个方法抽成宏。我们可以把这两个宏单独放到一个文件里面，这里以后需要进行数据持久化的模型都可以直接使用这两个宏。
#define encodeRuntime(A) \
\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [self valueForKey:key];\
[aCoder encodeObject:value forKey:key];\
}\
free(ivars);\
\

#define initCoderRuntime(A) \
\
if (self = [super init]) {\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [aDecoder decodeObjectForKey:key];\
[self setValue:value forKey:key];\
}\
free(ivars);\
}\
return self;\
\


@implementation Movie

- (void)encodeWithCoder:(NSCoder *)aCoder {
    encodeRuntime(Movie)

/*
    unsigned int count = 0;
    Ivar * ivarList = class_copyIvarList(self.class, &count);
    for (int i = 0; i < count; i++) {
        // 取出i位置对应的成员变量
        Ivar var = ivarList[i];
        // 查看成员变量
        const char * name = ivar_getName(var);
        // 归档
        NSString * key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [aCoder encodeObject:value forKey:key];
    }
    free(ivarList);
*/
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    initCoderRuntime(Movie)
    
/*
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar * ivarList = class_copyIvarList(self.class, &count);
        for (int i = 0; i < count; i++) {
            // 取出i位置对应的成员变量
            Ivar var = ivarList[i];
            // 查看成员变量
            const char * name = ivar_getName(var);
            // 归档
            NSString * key = [NSString stringWithUTF8String:name];
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(ivarList);
    }
    return self;
*/
}




- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
/*
 答案：都输出 Movie
 class 获取当前方法的调用者的类，superClass 获取当前方法的调用者的父类，super 仅仅是一个编译指示器，就是给编译器看的，不是一个指针。
 本质：只要编译器看到super这个标志，就会让当前对象去调用父类方法，本质还是当前对象在调用
 这个题目主要是考察关于objc中对 self 和 super 的理解：
    self 是类的隐藏参数，指向当前调用方法的这个类的实例。而 super 本质是一个编译器标示符，和 self 是指向的同一个消息接受者
    当使用 self 调用方法时，会从当前类的方法列表中开始找，如果没有，就从父类中再找；
    而当使用 super时，则从父类的方法列表中开始找，然后调用父类的这个方法。
 
 调用 [self class] 时，会转化成 objc_msgSend 函数      id objc_msgSend(id self, SEL op, ...)
 调用 [super class]时，会转化成 objc_msgSendSuper函数  id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
 第一个参数是 objc_super 这样一个结构体，其定义如下
 struct objc_super {
 __unsafe_unretained id receiver;
 __unsafe_unretained Class super_class;
 };
 
 第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self
 第二个成员是记录当前类的父类是什么，告诉程序从父类中开始找方法，找到方法后，最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用， 此时已经和[self class]调用相同了，故上述输出结果仍然返回 Son
 
 objc Runtime 开源代码对- (Class)class方法的实现
 -(Class)class { return object_getClass(self);}
 */



@end
